package com.aat.restful.jersey.demo2.netty;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URI;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.ws.rs.core.SecurityContext;

import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.internal.PropertiesDelegate;
import org.glassfish.jersey.server.ApplicationHandler;
import org.glassfish.jersey.server.ContainerException;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ContainerResponse;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spi.Container;
import org.glassfish.jersey.server.spi.ContainerResponseWriter;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;

public class JerseyServletHandler extends SimpleChannelInboundHandler<FullHttpRequest> implements Container {

	private ApplicationHandler application;

	public JerseyServletHandler(ApplicationHandler application) {
		this.application = application;
	}

	@Override
	public ApplicationHandler getApplicationHandler() {
		return application;
	}

	@Override
	public ResourceConfig getConfiguration() {
		return application.getConfiguration();
	}

	@Override
	public void reload() {
		reload(application.getConfiguration());
	}

	@Override
	public void reload(ResourceConfig arg0) {
		// TODO Auto-generated method stub
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
		HttpRequest request = msg;
		String base = request.uri();
		final URI baseUri = new URI(base);
		final URI requestUri = new URI(base.substring(0, base.length() - 1) + request.uri());
		SecurityContext securityContext=new SecurityContext() {
			
			@Override
			public boolean isUserInRole(String arg0) {
				// TODO Auto-generated method stub
				return false;
			}
			
			@Override
			public boolean isSecure() {
				// TODO Auto-generated method stub
				return false;
			}
			
			@Override
			public Principal getUserPrincipal() {
				// TODO Auto-generated method stub
				return null;
			}
			
			@Override
			public String getAuthenticationScheme() {
				// TODO Auto-generated method stub
				return null;
			}
		};
		 PropertiesDelegate properties = new MapPropertiesDelegate();

		 ContainerRequest containerRequest = new ContainerRequest(
	                baseUri,
	                requestUri,
	                request.method().name(),
	                securityContext,
	                properties
	        );
		 containerRequest.setWriter(new Writer(ctx.channel()));
		 application.handle(containerRequest);
	}

	private final static class Writer implements ContainerResponseWriter {
		private FullHttpResponse response;
		private final Channel channel;

		private Writer(Channel channel) {
			this.channel = channel;
		}

		@Override
		public OutputStream writeResponseStatusAndHeaders(long l, ContainerResponse containerResponse) throws ContainerException {
			response=new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(containerResponse.getStatus()));
			for(Map.Entry<String, List<Object>> e :  containerResponse.getHeaders().entrySet()){
				 List<String> values = new ArrayList<String>();
				 for(Object v:e.getValue()){
					 values.add(containerResponse.getHeaderString(v.toString()));
				 }
				 response.headers().set(e.getKey(), values);
			}
			
			channel.writeAndFlush(response);
			
			
			ByteBuf buf= response.content();
			
			buf.alloc();
			byte[] bs=buf.array();
			ByteArrayOutputStream bos=new ByteArrayOutputStream();
			try {
				bos.write(bs,0,buf.capacity());
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			return bos;
		}

		@Override
		public void commit() {
			channel.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
		}

		@Override
		public boolean enableResponseBuffering() {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public void failure(Throwable arg0) {
			channel.close();
		}

		@Override
		public void setSuspendTimeout(long arg0, TimeUnit arg1) throws IllegalStateException {
			// TODO Auto-generated method stub

		}

		@Override
		public boolean suspend(long arg0, TimeUnit arg1, TimeoutHandler arg2) {
			// TODO Auto-generated method stub
			return false;
		}

	}

}
